package com.ylkj.core.dao.mybatis.impl;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import com.ylkj.common.exception.BaseDaoException;
import com.ylkj.common.util.bean.BeanMapUtil;
import com.ylkj.common.util.reflect.ReflectGeneric;
import com.ylkj.core.dao.mybatis.IBaseGenericDAO;
import com.ylkj.core.page.GenericDefaultPage;
import com.ylkj.core.page.IGenericPage;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import com.github.miemiedev.mybatis.paginator.domain.Order;
import com.github.miemiedev.mybatis.paginator.domain.PageBounds;
import com.github.miemiedev.mybatis.paginator.domain.PageList;

/**
 * 基于Mybatis的基础泛型DAO实现类。
 * 
 * @author 呼云飞
 * 
 * @param <T> 业务实体类型
 * @param <ID> ID类型 ，如：String、Long、Integer 等
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public abstract class BaseGenericDAOImpl<T, ID extends Serializable> implements IBaseGenericDAO<T, ID> {
	

	/*多数据源模式开始*/
	@Autowired(required = false)
	@Qualifier("sqlSessionDefault")
	private SqlSessionTemplate sqlSessionDefault;
	
	protected abstract SqlSessionTemplate getSession();
	
	private SqlSessionTemplate getSqlSession(){
		if(this.getSession() != null){
			return this.getSession();
		}
		return sqlSessionDefault;
	}
	
	/*多数据源模式结束*/
	
	
	/*单个数据源模式
	@Autowired(required = false)
	public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory){
		super.setSqlSessionFactory(sqlSessionFactory);
	}*/
	
	public static final String SQLNAME_SEPARATOR = ".";
	public static final String SQL_SAVE = "save";   
	public static final String SQL_UPDATE = "update";   
	public static final String SQL_UPDATE_DYNAMIC="update_dynamic";
	public static final String SQL_GETBYID = "getById";
	public static final String SQL_GETBYMOBILE = "getByMobile";
	public static final String SQL_GETBYUID = "getByUid";
	public static final String SQL_DELETEBYID = "deleteById";
	public static final String SQL_DELETEBYUID = "deleteByUid";
	public static final String SQL_DELETEBYIDS = "deleteByIds";
	public static final String SQL_FINDPAGEBY = "findPageBy";
	public static final String SQL_FINDLISTBY = "findListBy";
	public static final String SQL_GETCOUNTBY = "getCountBy";

	private static final String SORT_NAME = "SORT";
	
	private static final String DIR_NAME = "DIR";
	/** 不能用于SQL中的非法字符（主要用于排序字段名） */
	public static final String[] ILLEGAL_CHARS_FOR_SQL = {",", ";", " ", "\"", "%"};

	/**
	 * 获取默认SqlMapping命名空间。
	 * 使用泛型参数中业务实体类型的全限定名作为默认的命名空间。
	 * 如果实际应用中需要特殊的命名空间，可由子类重写该方法实现自己的命名空间规则。
	 * @return 返回命名空间字符串
	 */
	protected String getDefaultSqlNamespace() {
		Class<T> clazz = ReflectGeneric.getClassGenricType(this.getClass());
		String nameSpace = clazz.getName();
		return nameSpace;
	}

	/**
	 * 将SqlMapping命名空间与给定的SqlMapping名组合在一起。
	 * @param sqlName SqlMapping名
	 * @return 组合了SqlMapping命名空间后的完整SqlMapping名
	 */
	protected String getSqlName(String sqlName) {
		return sqlNamespace + SQLNAME_SEPARATOR + sqlName;
	}

	/**
	 * SqlMapping命名空间
	 */
	private String sqlNamespace = getDefaultSqlNamespace();

	/**
	 * 获取SqlMapping命名空间
	 * @return SqlMapping命名空间
	 */
	public String getSqlNamespace() {
		return sqlNamespace;
	}

	/**
	 * 设置SqlMapping命名空间。
	 * 此方法只用于注入SqlMapping命名空间，以改变默认的SqlMapping命名空间，
	 * 不能滥用此方法随意改变SqlMapping命名空间。
	 * @param sqlNamespace SqlMapping命名空间
	 */
	public void setSqlNamespace(String sqlNamespace) {
		this.sqlNamespace = sqlNamespace;
	}

	/**
	 * 生成主键值。
	 * 默认情况下什么也不做；
	 * 如果需要生成主键，需要由子类重写此方法根据需要的方式生成主键值。
	 * @param ob 要持久化的对象
	 */
	protected void generateId(T ob) {
		
	}

	public Integer save(T ob) {
		generateId(ob);
		return this.getSqlSession().insert(
				getSqlName(SQL_SAVE), ob);
	}

	public Integer update(T ob) {
		return this.getSqlSession().update(
				getSqlName(SQL_UPDATE), ob);
	}
	public Integer updateDynamic(T ob) {
		return this.getSqlSession().update(
				getSqlName(SQL_UPDATE_DYNAMIC), ob);
	}
	
	public T getById(ID id) {
		return (T) this.getSqlSession().selectOne(
				getSqlName(SQL_GETBYID), id);
	}

	public T getByUid(String uid) {
		return (T) this.getSqlSession().selectOne(
				getSqlName(SQL_GETBYUID), uid);
	}

	public T getByMobile(String mobile) {
		return (T) this.getSqlSession().selectOne(
				getSqlName(SQL_GETBYMOBILE), mobile);
	}
	
	public Integer deleteByIds(ID[] ids) {
		return this.getSqlSession().delete(
				getSqlName(SQL_DELETEBYIDS), ids);
	}

	
	public Integer deleteById(ID id){
		return this.getSqlSession().delete(
				getSqlName(SQL_DELETEBYID), id);
	}

	public Integer deleteByUId(String uid){
		return this.getSqlSession().delete(
				getSqlName(SQL_DELETEBYUID), uid);
	}
	
	public <Many> IGenericPage<Many> findPageBy(Map<String, Object> params, int pageNo, int pageSize, String sort, String dir){
		PageBounds pageBounds = null;
		if (StringUtils.isNotEmpty(sort) && StringUtils.isNotEmpty(dir)) {
			pageBounds = new PageBounds(pageNo,pageSize,Order.create(sort, dir));
		}else{
			//分页PageBounds已经自动计算好了，所以调用者只需要指定 当前页数和每页长度即可
			pageBounds = new PageBounds(pageNo,pageSize);
		}
		//是否返回总数(设置为true会执count语句)
		pageBounds.setContainsTotalCount(true);
		List list = getSqlSession().selectList(getSqlName(SQL_FINDPAGEBY), params, pageBounds);
		PageList pageList = (PageList) list;
		int count = pageList.getPaginator().getTotalCount();
		GenericDefaultPage page = new GenericDefaultPage<T>(pageNo, pageSize, list, count);
		page.setTotalPage(page.getLastPageNo());
		return page;
	}
	
	public <Many> IGenericPage<Many> findPageBy(Map<String, Object> params, PageBounds pageBounds){
		List list = getSqlSession().selectList(getSqlName(SQL_FINDPAGEBY), params, pageBounds);
		PageList pageList = (PageList) list;
		int count = pageList.getPaginator().getTotalCount();
		GenericDefaultPage page = new GenericDefaultPage<T>(pageBounds.getPage(), pageBounds.getLimit(), list, count);
		page.setTotalPage(page.getLastPageNo());
		return page;
	    
	}
			
	
	public IGenericPage<T> findPageBy(
			T param, int pageNo, int pageSize, String sort, String dir) {
		int count = getCountBy(param);
		if (count < 1) {
			return GenericDefaultPage.emptyPage();
		}
		Map<String, Object> paramMap = null;
		try{
			paramMap = BeanMapUtil.bean2Map(param);
		}catch(Exception e){
			throw new BaseDaoException("获取参数失败", e);
		}
		// 排序条件
		if (sort != null) {
			// 排序字段不为空，过滤其中可能存在的非法字符
			sort = filterIllegalChars(sort, ILLEGAL_CHARS_FOR_SQL);
		}
		if (StringUtils.isEmpty(sort) || StringUtils.isEmpty(dir)) {
		} else {
			paramMap.put(SORT_NAME, sort);
			paramMap.put(DIR_NAME, dir);
		}
		// 分页条件
		//int start = GenericDefaultPage.getStartOfPage(pageNo, pageSize) - 1;
		RowBounds rowBound = new RowBounds(pageNo, pageSize);
		List<T> lst = getSqlSession().selectList(getSqlName(SQL_FINDPAGEBY), paramMap, rowBound);
		GenericDefaultPage page = new GenericDefaultPage<T>(pageNo, pageSize, lst, count);
		page.setTotalPage(page.getLastPageNo());
		return page;
	}

	
	public Integer getCountBy(T param) {
		Map<String, Object> paramMap = null;
		try{
			paramMap = BeanMapUtil.bean2Map(param);
		}catch(Exception e){
			throw new BaseDaoException("获取参数失败", e);
		}
		return (Integer)this.getSqlSession().selectOne(
				getSqlName(SQL_GETCOUNTBY), paramMap);
	}

	
	public List<T> findListBy(T param, String sort, String dir) {
		Map<String, Object> paramMap = null;
		try{
			paramMap = BeanMapUtil.bean2Map(param);
		}catch(Exception e){
			throw new BaseDaoException("获取参数失败", e);
		}
		// 排序条件
		if (sort != null) {
			// 排序字段不为空，过滤其中可能存在的非法字符
			sort = filterIllegalChars(sort, ILLEGAL_CHARS_FOR_SQL);
		}
		if (StringUtils.isEmpty(sort) || StringUtils.isEmpty(dir)) {
		} else {
			paramMap.put(SORT_NAME, sort);
			paramMap.put(DIR_NAME, dir);
		}
		List<T> lst = getSqlSession().selectList(getSqlName(SQL_FINDLISTBY), paramMap);
		return lst;
	}
	
	public List<T> findListBy(T param){
		return findListBy(param, null, null);
	}
	/**
	 * 从给定字符串中将指定的非法字符串数组中各字符串过滤掉。
	 * @param str 待过滤的字符串
	 * @param filterChars 指定的非法字符串数组
	 * @return 过滤后的字符串
	 */
	protected String filterIllegalChars(String str, String[] filterChars) {
		String rs = str;
		if (rs != null && filterChars != null) {
			for (String fc : filterChars) {
				if (fc != null && fc.length() > 0) {
					str = str.replaceAll(fc, ""); 
				}
			}
		}
		return rs;
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#insert(String, Object)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @param parameter 参数
	 * @return 执行结果——插入成功的记录数
	 * @see org.apache.ibatis.session.SqlSession#insert(String, Object)
	 */
	protected int insert(String statement, Object parameter) {
		return this.getSqlSession().insert(
				getSqlName(statement), parameter);
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#insert(String)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @return 执行结果——插入成功的记录数
	 * @see org.apache.ibatis.session.SqlSession#insert(String)
	 */
	protected int insert(String statement) {
		return this.getSqlSession().insert(
				getSqlName(statement));
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#update(String, Object)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @param parameter 参数
	 * @return 执行结果——更新成功的记录数
	 * @see org.apache.ibatis.session.SqlSession#update(String, Object)
	 */
	protected int update(String statement, Object parameter) {
		return this.getSqlSession().update(
				getSqlName(statement), parameter);
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#update(String)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @return 执行结果——更新成功的记录数
	 * @see org.apache.ibatis.session.SqlSession#update(String)
	 */
	protected int update(String statement) {
		return this.getSqlSession().update(
				getSqlName(statement));
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#delete(String, Object)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @param parameter 参数
	 * @return 执行结果——删除成功的记录数
	 * @see org.apache.ibatis.session.SqlSession#delete(String, Object)
	 */
	protected int delete(String statement, Object parameter) {
		return this.getSqlSession().delete(
				getSqlName(statement), parameter);
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#delete(String)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @return 执行结果——删除成功的记录数
	 * @see org.apache.ibatis.session.SqlSession#delete(String)
	 */
	protected int delete(String statement) {
		return this.getSqlSession().delete(
				getSqlName(statement));
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#selectList(String, Object, RowBounds)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @param parameter 参数
	 * @param rowBounds 用于分页查询的记录范围
	 * @return 查询结果列表
	 * @see org.apache.ibatis.session.SqlSession#selectList(String, Object, RowBounds)
	 */
	protected List selectList(
			String statement, Object parameter, RowBounds rowBounds) {
		return this.getSqlSession().selectList(
				getSqlName(statement), parameter, rowBounds);
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#selectList(String, Object)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @param parameter 参数
	 * @return 查询结果列表
	 * @see org.apache.ibatis.session.SqlSession#selectList(String, Object)
	 */
	protected List selectList(String statement, Object parameter) {
		return this.getSqlSession().selectList(
				getSqlName(statement), parameter);
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#selectList(String)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @return 查询结果列表
	 * @see org.apache.ibatis.session.SqlSession#selectList(String)
	 */
	protected List selectList(String statement) {
		return this.getSqlSession().selectList(
				getSqlName(statement));
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#selectOne(String, Object)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @param parameter 参数
	 * @return 查询结果对象
	 * @see org.apache.ibatis.session.SqlSession#selectOne(String, Object)
	 */
	protected Object selectOne(String statement, Object parameter) {
		return this.getSqlSession().selectOne(
				getSqlName(statement), parameter);
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#selectOne(String)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @return 查询结果对象
	 * @see org.apache.ibatis.session.SqlSession#selectOne(String)
	 */
	protected Object selectOne(String statement) {
		return this.getSqlSession().selectOne(
				getSqlName(statement));
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#selectMap(String, Object, String, RowBounds)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @param parameter 参数
	 * @param mapKey 数据mapKey
	 * @param rowBounds 用于分页查询的记录范围
	 * @return 查询结果Map
	 * @see org.apache.ibatis.session.SqlSession#selectMap(String, Object, String, RowBounds)
	 */
	protected Map selectMap(
			String statement, Object parameter, String mapKey,
			RowBounds rowBounds) {
		return this.getSqlSession().selectMap(
				getSqlName(statement),
				parameter, mapKey, rowBounds);
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#selectMap(String, Object, String)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @param parameter 参数
	 * @param mapKey 数据mapKey
	 * @return 查询结果Map
	 * @see org.apache.ibatis.session.SqlSession#selectMap(String, Object, String)
	 */
	protected Map selectMap(
			String statement, Object parameter, String mapKey) {
		return this.getSqlSession().selectMap(
				getSqlName(statement), parameter, mapKey);
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#selectMap(String, String)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @param mapKey 数据mapKey
	 * @return 查询结果Map
	 * @see org.apache.ibatis.session.SqlSession#selectMap(String, String)
	 */
	protected Map selectMap(String statement, String mapKey) {
		return this.getSqlSession().selectMap(
				getSqlName(statement), mapKey);
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#select(String, Object, RowBounds, ResultHandler)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @param parameter 参数
	 * @param rowBounds 用于分页查询的记录范围
	 * @param handler 结果集处理器
	 * @see org.apache.ibatis.session.SqlSession#select(String, Object, RowBounds, ResultHandler)
	 */
	protected void select(
			String statement, Object parameter, RowBounds rowBounds,
			ResultHandler handler) {
		this.getSqlSession().select(
				getSqlName(statement),
				parameter, rowBounds, handler);
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#select(String, Object, ResultHandler)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @param parameter 参数
	 * @param handler 结果集处理器
	 * @see org.apache.ibatis.session.SqlSession#select(String, Object, ResultHandler)
	 */
	protected void select(
			String statement, Object parameter, ResultHandler handler) {
		this.getSqlSession().select(
				getSqlName(statement), parameter, handler);
	}

	/**
	 * 对{@link org.apache.ibatis.session.SqlSession#select(String, ResultHandler)}的代理。
	 * 将statement包装了命名空间，方便DAO子类调用。
	 * @param statement 映射的语句ID
	 * @param handler 结果集处理器
	 * @see org.apache.ibatis.session.SqlSession#select(String, ResultHandler)
	 */
	protected void select(String statement, ResultHandler handler) {
		this.getSqlSession().select(
				getSqlName(statement), handler);
	}

}
